;>*******************************************************************************************************************************
;*																																*
;*	File:	Random.asm																											*
;*																																*
;*	This file contains a fast random number generator.																			*
;*																																*
;*	The algorithm is known as the Mersenne Twister, or MT19937.  See <http://en.wikipedia.org/wiki/Mersenne_twister> and		*
;*	<http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/emt19937ar.html> for details on the algorithm.						*
;*																																*
;*	NOTE: This does not provide its own mutual exclusion for multithreading.													*
;*																																*
;*	See Also:																													*
;*		- Random.inc																											*
;*																																*
;<*******************************************************************************************************************************

include	Random.inc

.code
;>*******************************************************************************************************************************
;*																																*
;*	Function:	SetRandomSeed																									*
;*																																*
;*	Initializes the numbers array based on the provided seed.																	*
;*																																*
;*	Parameters:																													*
;*	pRandom	- address of the random number object																				*
;*	seed	- the random seed																									*
;*																																*
;<*******************************************************************************************************************************
SetRandomSeed	proc	pRandom:PTR RANDOM,seed:DWORD
	xor		ecx,ecx
	push	ebx
	push	esi
	mov		esi,pRandom
ASSUME ESI:PTR RANDOM
	mov		eax,seed			;RandomNumbers[0] = seed
	mov		ebx,1812433253
	mov		[esi].numberOffset,RANDOM_ARRAY_SIZE*4	;indicates to call GenerateRandom on first call to GetRandomNumber
NextRandom:
	mov		[esi].numbers[ecx*4],eax
	inc		ecx
	cmp		ecx,RANDOM_ARRAY_SIZE
	je		Done
	mov		edx,eax
	shr		edx,30
	xor		eax,edx
	mul		ebx					;RandomNumbers[isDoneGenerating] = 1812433253*(RandomNumbers[isDoneGenerating-1] XOR (RandomNumbers[isDoneGenerating-1] >>> 30)) + isDoneGenerating
	add		eax,ecx				;(this isDoneGenerating the newer version of the initialization formula)
	jmp		NextRandom
Done:
	pop		esi
	pop		ebx
	ret
ASSUME ESI:NOTHING
SetRandomSeed	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	GetRandomNumber																									*
;*																																*
;*	Returns a uniformly-distributed random integer between 0 and 0FFFFFFFFh														*
;*																																*
;*	Parameters:																													*
;*	pRandom	- address of the random number object																				*
;*																																*
;*	Returns:																													*
;*	eax	- uniformly-distributed random DWORD																					*
;*																																*
;<*******************************************************************************************************************************
GetRandomNumber	proc	pRandom:PTR RANDOM
	push	esi
	mov		esi,pRandom
ASSUME ESI:PTR RANDOM
	mov		eax,[esi].numberOffset
	cmp		eax,RANDOM_ARRAY_SIZE*4
	jne		NoRegenerate
	call	GenerateRandom
	xor		eax,eax
NoRegenerate:
	push	eax
	mov		eax,[esi].numbers[eax]
	mov		ecx,eax			; y = y XOR (y>>>11)
	shr		ecx,11			;
	xor		eax,ecx			;
	mov		ecx,eax			; y = y XOR ((y<<7) & 9D2C5680h)
	shl		ecx,7			;
	and		ecx,9D2C5680h	;
	xor		eax,ecx			;
	mov		ecx,eax			; y = y XOR ((y<<15) & EFC60000h)
	shl		ecx,15			;
	and		ecx,0EFC60000h	;
	xor		eax,ecx			;
	mov		ecx,eax			; y = y XOR (y>>>18)
	shr		ecx,18			;
	xor		ecx,eax			;
	
	pop		eax
	add		eax,4
	mov		[esi].numberOffset,eax
	mov		eax,ecx
	pop		esi
	ret
ASSUME ESI:NOTHING
GetRandomNumber	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	GenerateRandom																									*
;*																																*
;*	Regenerates the RandomNumbers array after a pass though.																	*
;*																																*
;*	Parameters:																													*
;*	esi	- address of the random number object																					*
;*																																*
;<*******************************************************************************************************************************
GenerateRandom	proc
ASSUME ESI:PTR RANDOM
	push	ebx
	push	edi
	xor		eax,eax
	xor		edi,edi
	mov		ecx,397*4
Next:
	mov		edx,[esi].numbers[eax]
	shr		edx,31
	mov		ebx,[esi].numbers[eax][4]
	and		ebx,7FFFFFFFh
	add		edx,ebx
	shr		edx,1
	jnc		SetValue
	xor		edx,9908B0DFh
SetValue:
	xor		edx,[esi].numbers[ecx]
	mov		[esi].numbers[eax],edx
	add		eax,4
	add		ecx,4
	cmp		eax,(RANDOM_ARRAY_SIZE-1)*4
	je		OutOfLoop
Check2ndIndex:
	cmp		ecx,RANDOM_ARRAY_SIZE*4
	cmove	ecx,edi
	jmp		Next

OutOfLoop:
	mov		edx,[esi].numbers[(RANDOM_ARRAY_SIZE-1)*4]
	shr		edx,1
	mov		eax,[esi].numbers[0*4]
	and		eax,7FFFFFFFh
	add		edx,eax
	shr		edx,1
	jnc		SetValue2
	xor		edx,9908B0DFh
SetValue2:
	xor		edx,[esi].numbers[396*4]
	mov		[esi].numbers[(RANDOM_ARRAY_SIZE-1)*4],edx
	pop		edi
	pop		ebx
	ret
ASSUME ESI:NOTHING
GenerateRandom	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	GetBoundedRandomNumber																							*
;*																																*
;*	Returns a uniformly-distributed random integer between 0 (inclusive) and eax (exclusive)									*
;*																																*
;*	Parameters:																													*
;*	pRandom	- address of the random number object																				*
;*	eax		- upper bound on the number																							*
;*																																*
;*	Returns:																													*
;*	eax	- bounded, uniformly-distributed random integer																			*
;*																																*
;<*******************************************************************************************************************************
GetBoundedRandomNumber	proc	pRandom:PTR RANDOM
	push	eax
	invoke	GetRandomNumber,pRandom
	pop		edx
	mul		edx
	mov		eax,edx
	ret
GetBoundedRandomNumber	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	GetRandomFloat																									*
;*																																*
;*	Returns a uniformly-distributed random 4-byte floating-point number between 0.0 (inclusive) and 1.0 (exclusive)				*
;*																																*
;*	Not all numbers in that range can be generated by this function, as the calculation is of the form u' = u / (2^24), where u	*
;*	is a uniformly-distributed random integer between 0 (inclusive) and 2^24 (exclusive).  For example, non-zero numbers less	*
;*	than 2^(-24) will not appear.																								*
;*																																*
;*	Parameters:																													*
;*	pRandom	- address of the random number object																				*
;*																																*
;*	Returns:																													*
;*	xmm0	- uniformly-distributed random float between 0.0 (inclusive) and 1.0 (exclusive) in first element					*
;*																																*
;<*******************************************************************************************************************************
GetRandomFloat	proc	pRandom:PTR RANDOM
	invoke		GetRandomNumber,pRandom
	and			eax,0FFFFFFh	;keep 24 bits for range 0 to (2^24)-1
	xor			ecx,ecx
	cvtsi2ss	xmm0,eax
	mulss		xmm0,FloatReductionFactor
	ret
GetRandomFloat	endp

